package org.dtrd.modules.achievement.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONArray;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.apache.tomcat.util.bcel.Const;
import org.dtrd.common.util.DateUtils;
import org.dtrd.config.system.Constant;
import org.dtrd.config.system.Constant.SignInPlanUsageStatus;
import org.dtrd.config.system.Constant.SigninItemPlanStatus;
import org.dtrd.modules.achievement.entity.bean.*;
import org.dtrd.modules.achievement.entity.po.DtrdEntRdSigninPlan;
import org.dtrd.modules.achievement.entity.po.DtrdEntRdSigninTemplate;
import org.dtrd.modules.achievement.mapper.DtrdEntRdSigninPlanMapper;
import org.dtrd.modules.achievement.service.IDtrdEntRdSigninPlanService;
import org.dtrd.modules.achievement.service.IDtrdEntRdSigninRecordService;
import org.dtrd.modules.achievement.service.IDtrdEntRdSigninTemplateService;
import org.dtrd.modules.system.service.ISysUserService;
import org.dtrd.modules.util.ShiroUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.text.ParseException;
import java.util.*;

/**
 * @author 萧
 * @date 2021-09-21 22:50:23
 **/
@Service
@DS("multi-datasource1")
@Slf4j
public class DtrdEntRdSigninPlanServiceImpl extends ServiceImpl<DtrdEntRdSigninPlanMapper, DtrdEntRdSigninPlan> implements IDtrdEntRdSigninPlanService {

    @Autowired
    private IDtrdEntRdSigninTemplateService signinTemplateService;
    @Autowired
    private ISysUserService sysUserService;
    @Autowired
    private IDtrdEntRdSigninRecordService signinRecordService;


    @Override
    public Map<Integer, Map<Integer, Integer>> getPatientSigninCountByType(Integer patientId) {
        LambdaQueryWrapper<DtrdEntRdSigninPlan> wrapper = Wrappers.lambdaQuery(DtrdEntRdSigninPlan.class)
                .eq(DtrdEntRdSigninPlan::getIsDel,0)
                .eq(DtrdEntRdSigninPlan::getPatientId, patientId)
                .eq(DtrdEntRdSigninPlan::getSignInStatus, Constant.SignInPlanUsageStatus.Using.getType());
        DtrdEntRdSigninPlan signinPlan = getOne(wrapper);
        if (signinPlan != null) {
            HashMap<Integer, Map<Integer, Integer>> map = new HashMap<>();
            // 周日
            Map<Integer, Integer> map7 = handleSignPlanJson(signinPlan.getSignInJsonSunday(), signinPlan.getDataId());
            map.put(1, map7);
            // 周一
            Map<Integer, Integer> map1 = handleSignPlanJson(signinPlan.getSignInJsonMonday(), signinPlan.getDataId());
            map.put(2, map1);
            // 周二
            Map<Integer, Integer> map2 = handleSignPlanJson(signinPlan.getSignInJsonTuesday(), signinPlan.getDataId());
            map.put(3, map2);
            // 周三
            Map<Integer, Integer> map3 = handleSignPlanJson(signinPlan.getSignInJsonWednesday(), signinPlan.getDataId());
            map.put(4, map3);
            // 周四
            Map<Integer, Integer> map4 = handleSignPlanJson(signinPlan.getSignInJsonThursday(), signinPlan.getDataId());
            map.put(5, map4);
            // 周五
            Map<Integer, Integer> map5 = handleSignPlanJson(signinPlan.getSignInJsonFriday(), signinPlan.getDataId());
            map.put(6, map5);
            // 周六
            Map<Integer, Integer> map6 = handleSignPlanJson(signinPlan.getSignInJsonSaturday(), signinPlan.getDataId());
            map.put(7, map6);
            return map;
        }
        return null;
    }

    private Map<Integer, Integer> handleSignPlanJson(String signPlanJson, Integer id) {
        // 10个打卡项目
        Map<Integer, Integer> map = new HashMap<>(10);
        try {
            if (StrUtil.isNotBlank(signPlanJson)) {
                List<SignInAction> actions = JSONArray.parseArray(signPlanJson, SignInAction.class);
                actions.stream().filter(action -> StrUtil.isNotBlank(action.getStarttime()) && StrUtil.isNotBlank(action.getEndtime()))
                        .forEach(action -> {
                            Integer recordType = action.getRecordType();
                            Integer count = map.getOrDefault(recordType, 0);
                            map.put(recordType, ++count);
                        });
            }
        } catch (Exception e) {
            log.error("打卡计划json格式错误, 记录ID: {}", id);
        }
        return map;
    }

    @Override
    public Map<Integer, Map<Integer, Map<Integer, Integer>>> listPatientsSigninCountByType(List<Integer> patientIds) {
        if (CollectionUtil.isNotEmpty(patientIds)) {
            Map<Integer, Map<Integer, Map<Integer, Integer>>> result = new HashMap<>(patientIds.size());
            for (Integer patientId : patientIds) {
                Map<Integer, Map<Integer, Integer>> map = getPatientSigninCountByType(patientId);
                if (map != null) {
                    result.put(patientId, map);
                }
            }
            return result;
        }
        return null;
    }

    @Override
    public boolean saveSigninPlan(SigninPlan request) {
        DtrdEntRdSigninPlan entity = new DtrdEntRdSigninPlan().init().fromSigninPlan(request);
        return saveOrUpdate(entity);
    }

    @Override
    public Date getSigninPlanStartDate(Integer patientId) {
        LambdaQueryWrapper<DtrdEntRdSigninPlan> wrapper = Wrappers.lambdaQuery(DtrdEntRdSigninPlan.class)
                .eq(DtrdEntRdSigninPlan::getPatientId, patientId)
                .orderByDesc(DtrdEntRdSigninPlan::getCreateTime)
                .last("limit 1");
        DtrdEntRdSigninPlan plan = getOne(wrapper);
        return Optional.ofNullable(plan).map(DtrdEntRdSigninPlan::getSignInStartdate).orElse(null);
    }

    /**
     * 根据用户id与计划日期查询打卡计划
     *
     * @param patientId
     * @param planDate
     * @return
     */
    @Override
    public SigninPlan getSigninPlan(Integer patientId, String planDate) {
        // 查询打卡计划
        if (planDate == null) {
            planDate = DateUtils.getDate(DateUtils.date_sdf.get().toPattern());
        }
        LambdaQueryWrapper<DtrdEntRdSigninPlan> query = new LambdaQueryWrapper<>();
        query.eq(DtrdEntRdSigninPlan::getIsDel, 0)
                .eq(DtrdEntRdSigninPlan::getPatientId, patientId)
                .in(DtrdEntRdSigninPlan::getSignInStatus, SignInPlanUsageStatus.Using.getType(), SignInPlanUsageStatus.NotStart.getType())
                .le(DtrdEntRdSigninPlan::getSignInStartdate, planDate)
                .ge(DtrdEntRdSigninPlan::getSignInEnddate, planDate)
                .orderByDesc(DtrdEntRdSigninPlan::getCreateTime)
                .last("limit 1");
        DtrdEntRdSigninPlan plan = baseMapper.selectOne(query);
        if (plan != null) {
            // 进一步判断工作日及周末
            Calendar calendar;
            try {
                calendar = DateUtils.parseCalendar(planDate, DateUtils.date_sdf.get().toPattern());
            } catch (ParseException e) {
                // 默认取当前时间
                calendar = DateUtils.getCalendar();
            }
            SigninPlan signinPlan = new SigninPlan().fromPO(plan);
            // 模版名字
            if (plan.getSignInTemplateId() != null) {
                signinPlan.setSignInTemplateName(signinTemplateService.getTemplateNameById(plan.getSignInTemplateId()));
                log.info("DtrdEntRdSigninPlanServiceImpl.getSigninPlan.[patientId, planDate] "+signinPlan.getSignInTemplateName());
            }
            int week = calendar.get(Calendar.DAY_OF_WEEK);
            String signinJson;
            switch (week) {
                case Calendar.SUNDAY:
                    signinJson = plan.getSignInJsonSunday();
                    break;
                case Calendar.SATURDAY:
                    signinJson = plan.getSignInJsonSaturday();
                    break;
                case Calendar.FRIDAY:
                    signinJson = plan.getSignInJsonFriday();
                    break;
                case Calendar.THURSDAY:
                    signinJson = plan.getSignInJsonThursday();
                    break;
                case Calendar.WEDNESDAY:
                    signinJson = plan.getSignInJsonWednesday();
                    break;
                case Calendar.TUESDAY:
                    signinJson = plan.getSignInJsonTuesday();
                    break;
                default:
                    signinJson = plan.getSignInJsonMonday();
                    break;
            }
            if (signinJson == null) {
                signinJson = plan.getSignInJsonMonday();
            }
            signinPlan.setSignInJson(signinJson);
//            if (week == Calendar.SUNDAY || week == Calendar.SATURDAY) {
//                // 周末
//                if (plan.getSignInJsonWeekend() == null) {
//                    signinPlan.setSignInJsonWeekend(plan.getSignInJsonWorkingday());
//                }
//                signinPlan.setSignInJson(signinPlan.getSignInJsonWeekend());
//            }
            return signinPlan;
        }
        return null;
    }

    @Override
    public PatientSigninPlan getSigninPlan(Integer patientId) {
        LambdaQueryWrapper<DtrdEntRdSigninPlan> wrapper = Wrappers.lambdaQuery(DtrdEntRdSigninPlan.class)
                .eq(DtrdEntRdSigninPlan::getPatientId, patientId)
                .orderByDesc(DtrdEntRdSigninPlan::getSignInStatus)
                .eq(DtrdEntRdSigninPlan::getSignInStatus, Constant.SignInPlanUsageStatus.Using.getType())
                .orderByDesc(DtrdEntRdSigninPlan::getCreateTime)
                .last("limit 1");
        DtrdEntRdSigninPlan plan = getOne(wrapper);
        if(plan == null){
            wrapper = Wrappers.lambdaQuery(DtrdEntRdSigninPlan.class)
                    .eq(DtrdEntRdSigninPlan::getPatientId, patientId)
                    .orderByDesc(DtrdEntRdSigninPlan::getSignInStartdate)
                    .orderByAsc(DtrdEntRdSigninPlan::getSignInStatus)
                    .last("limit 1");
            plan = getOne(wrapper);
        }
        return Optional.ofNullable(plan)
                .map(po -> {
                    // 设置返回值
                    PatientSigninPlan vo = new PatientSigninPlan().fromPO(po);
                    // 模版名字
                    if (po.getSignInTemplateId() != null) {
                        vo.setSignInTemplateName(signinTemplateService.getTemplateNameById(po.getSignInTemplateId()));
                    }
                    // 更新人名字
                    if (StrUtil.isNotBlank(po.getUpdateBy())) {
                        String userName = sysUserService.getUserName(po.getUpdateBy());
                        vo.setEditor(userName);
                    }
                    // 模版状态文本
                    vo.setSignInStatusText(Constant.SignInPlanUsageStatus.valueOf(po.getSignInStatus()).getName());
                    return vo;
                }).orElse(null);
    }

    @Override
    public List<DtrdEntRdSigninPlan> getAllEffectivePlans() {
        return baseMapper.getAllEffectivePlans();
    }

    @Override
    public boolean updatePatientSignInPlanContent(PatientSigninPlan signinPlan) {
        Integer dataId = signinPlan.getDataId();
        DtrdEntRdSigninPlan plan = getById(dataId);
        if (plan == null) {
            return false;
        }
        plan = plan.getSignInPlanJson(signinPlan);
        return updateById(plan);
    }

    @Override
    public void savePatientSignInPlanFromTemplate(Integer patientId, Integer signInTemplateId, Date startTime, Date endTime) {
        DtrdEntRdSigninTemplate signinTemplate = signinTemplateService.getById(signInTemplateId);
        if (signinTemplate == null) {
            return;
        }
        DtrdEntRdSigninPlan po = new DtrdEntRdSigninPlan().parseFromTemplate(signinTemplate);
        po.setPatientId(patientId);
        po.setSignInTemplateId(signInTemplateId);
        po.setSignInStartdate(startTime);
        po.setSignInEnddate(endTime);
        boolean isFuture = DateUtils.isFuture(startTime);
        if (isFuture) {
            po.setSignInStatus(Constant.SignInPlanUsageStatus.NotStart.getType());
        } else {
            po.setSignInStatus(Constant.SignInPlanUsageStatus.Using.getType());
        }
//        LambdaQueryWrapper<DtrdEntRdSigninPlan> updateWrapper = Wrappers.lambdaQuery(DtrdEntRdSigninPlan.class)
//                .eq(DtrdEntRdSigninPlan::getPatientId, patientId);
//        saveOrUpdate(po, updateWrapper);
        saveOrUpdate(po);
        // 生成打卡记录
        if (!isFuture) {
            SigninRecord signinRecord = signinRecordService.getSigninRecordByPatientIdAndDate(patientId, null);
            if (signinRecord == null) {
                // 增加当前打卡记录项判断  当前没有打卡记录则实时生效
                signinRecordService.initSignInRecord(patientId);
            }
        }
    }

    @Override
    public boolean updatePatientSignInPlanContentByTemplateId(Integer templateId) {
        DtrdEntRdSigninTemplate template = signinTemplateService.getById(templateId);
        if (template == null) {
            return false;
        }
        LambdaQueryWrapper<DtrdEntRdSigninPlan> updateWrapper = Wrappers.lambdaQuery(DtrdEntRdSigninPlan.class)
                .eq(DtrdEntRdSigninPlan::getSignInTemplateId, templateId);
        List<DtrdEntRdSigninPlan> list = list(updateWrapper);
        if (CollectionUtil.isNotEmpty(list)) {
            list.forEach(po -> {
                po.setSignInJsonMonday(template.getSignInJsonMonday());
                po.setSignInJsonTuesday(template.getSignInJsonTuesday());
                po.setSignInJsonWednesday(template.getSignInJsonWednesday());
                po.setSignInJsonThursday(template.getSignInJsonThursday());
                po.setSignInJsonFriday(template.getSignInJsonFriday());
                po.setSignInJsonSaturday(template.getSignInJsonSaturday());
                po.setSignInJsonSunday(template.getSignInJsonSunday());
            });
            return updateBatchById(list);
        }
        return true;
    }

    @Override
    public boolean updatePatientSignInPlanStatus(Integer patientId, Integer status, String remark) {
        DtrdEntRdSigninPlan po = getOne(Wrappers.lambdaQuery(DtrdEntRdSigninPlan.class)
                .eq(DtrdEntRdSigninPlan::getPatientId, patientId));
        po.setSignInStatus(status);
        po.setRemark(remark);
        // 设置操作人
        String userId = ShiroUtil.getLoginUserId();
        po.setUpdateBy(userId);
        log.info("----------更新患者id：{}, 打卡状态：{}, 操作人id: {}", patientId, status, userId);
        return updateById(po);
    }

    @Override
    public boolean finishPatientSignInPlan(Integer patientId, Date startTime, Date endTime, String remark) {
        List<DtrdEntRdSigninPlan> list = getCurrentRecord(patientId, startTime, endTime);
        if (CollectionUtil.isNotEmpty(list)) {
            list.forEach(p -> {
                p.setSignInStatus(SignInPlanUsageStatus.Finished.getType());
                p.setRemark(remark);
            });
            return updateBatchById(list);
        }
        return false;
    }

    private List<DtrdEntRdSigninPlan> getCurrentRecord(Integer patientId, Date startTime, Date endTime) {
        LambdaQueryWrapper<DtrdEntRdSigninPlan> queryWrapper = baseQueryWrapper(patientId)
                .eq(DtrdEntRdSigninPlan::getSignInStartdate, startTime)
                .eq(DtrdEntRdSigninPlan::getSignInEnddate, endTime);
        return list(queryWrapper);
    }

    private LambdaQueryWrapper<DtrdEntRdSigninPlan> baseQueryWrapper(Integer patientId) {
        return Wrappers.lambdaQuery(DtrdEntRdSigninPlan.class)
                .eq(DtrdEntRdSigninPlan::getIsDel, 0)
                .eq(DtrdEntRdSigninPlan::getPatientId, patientId);
    }

}
